package com.powernode.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.powernode.constant.ManagerConstants;
import com.powernode.domain.SysMenu;
import com.powernode.ex.handler.BusinessException;
import com.powernode.mapper.SysMenuMapper;
import com.powernode.service.SysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
/**
 * 配置缓存的名称为"com.powernode.service.impl.SysMenuServiceImpl"
 * 该注解应用于类级别，用于统一指定缓存名称
 * 这样做可以简化缓存管理，提高性能
 */
@CacheConfig(cacheNames = "com.powernode.service.impl.SysMenuServiceImpl")
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Autowired
    private SysMenuMapper sysMenuMapper;


    @Override
    /**
     * 使用@Cacheable注解来缓存方法的返回值
     * 缓存的键由方法参数loginUserId生成，确保相同的loginUserId会使用相同的缓存数据
     * 这样做可以减少对数据库或服务的请求次数，提高系统性能
     */
    @Cacheable(key = "#loginUserId")
    public Set<SysMenu> queryUserMenuListByUserId(Long loginUserId) {
        //根据用户标识查询菜单权限集合
        Set<SysMenu> sysMenus = sysMenuMapper.selectUserMenusByUserId(loginUserId);
        //将菜单权限集合的数据转换为树结构（即：数据结构应该为层级关系的）
        return transformTree(sysMenus, 0L);
    }

    /**
     * 查询系统所有权限集合
     *
     * @return
     */
    @Override
    @Cacheable(key = ManagerConstants.SYS_ALL_MENU_KEY)
    public List<SysMenu> queryAllSysMenuList() {
        return sysMenuMapper.selectList(null);
    }

    /**
     * 保存系统菜单信息
     *
     * @param sysMenu 待保存的系统菜单对象
     * @return 返回保存结果，true表示保存成功，false表示保存失败
     */
    @Override
    @Cacheable(key = ManagerConstants.SYS_ALL_MENU_KEY)
    public Boolean saveSysMenu(SysMenu sysMenu) {
        // 插入系统菜单记录，如果插入成功则返回true，否则返回false
        return sysMenuMapper.insert(sysMenu) > 0;
    }

    /**
     * 修改菜单权限信息
     * <p>
     * 此方法用于更新系统菜单的信息它接受一个SysMenu对象作为参数，
     * 该对象包含了需要更新的菜单权限信息通过调用sysMenuMapper的updateById方法
     * 来实现更新操作如果更新成功，返回true；否则返回false
     *
     * @param sysMenu 包含了需要更新的菜单权限信息的对象
     * @return 更新操作是否成功true表示成功，false表示失败
     */
    @Override
    @Cacheable(key = ManagerConstants.SYS_ALL_MENU_KEY)
    public Boolean modifySysMenu(SysMenu sysMenu) {
        // 检查菜单类型，如果类型为0，则设置其父菜单ID为0，表示该菜单为顶级菜单
        Integer type = sysMenu.getType();
        if (0 == type) {
            sysMenu.setParentId(0L);
        }
        // 调用mapper的更新方法，返回更新操作是否成功
        return sysMenuMapper.updateById(sysMenu) > 0;
    }

    /**
     * 删除菜单权限
     * 如果说明当前菜单节点包含子节点，不可删除
     *
     * @param menuId 菜单ID
     * @return 删除结果，true表示删除成功，false表示删除失败
     * @throws BusinessException 当菜单节点包含子节点时抛出业务异常
     */
    @Override
    @Cacheable(key = ManagerConstants.SYS_ALL_MENU_KEY)
    public Boolean removeSysMenuById(Long menuId) {
        // 查询是否有子菜单
        List<SysMenu> sysMenuList = sysMenuMapper.selectList(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, menuId));
        // 如果存在子菜单，则抛出异常，不允许删除
        if (CollectionUtil.isNotEmpty(sysMenuList) && sysMenuList.size() != 0) {
            throw new BusinessException("当前菜单节点包含子节点集合，不可删除");
        }
        // 不存在子菜单,删除菜单，返回删除结果
        return sysMenuMapper.deleteById(menuId) > 0;
    }

    /**
     * 将集合转换为树结构
     * 此方法适用于
     * 1,已知菜单深度小于2的情况，
     * 2,但也能处理未知深度的菜单
     * 它通过递归地查找每个菜单项的子菜单，将扁平化的菜单集合转换为树形结构
     *
     * @param menus 扁平化的菜单集合，包含所有菜单项
     * @param pid   根菜单的父ID，通常为已知的起始点
     * @return 返回转换为树结构后的根菜单集合
     */
    private Set<SysMenu> transformTree(Set<SysMenu> menus, Long pid) {
        //1,已知菜单深度<2 从菜单集合中获取根节点集合
//        Set<SysMenu> roots = sysMenus.stream()
//                .filter(m -> m.getParentId().equals(pid))
//                .collect(Collectors.toSet());
//        //为每个根节点查找其子菜单，并设置子菜单列表
//        roots.forEach(root -> {
//            //查找当前根节点的所有子菜单
//            Set<SysMenu> child = sysMenus.stream().filter(m -> m.getParentId().equals(root.getMenuId())).collect(Collectors.toSet());
//            //将子菜单设置到当前根节点的子菜单列表中
//            root.setList(child);
//        });
//        2处理未知深度的菜单
        // 过滤出父ID等于pid的菜单项，收集到roots集合中
        // 获取根节点集合
        Set<SysMenu> roots = menus.stream().filter(menu -> menu.getParentId().equals(pid)).collect(Collectors.toSet());

        // 为每个根节点查找其子菜单，并设置子菜单列表
        // 遍历roots集合中的每个根节点

        // 递归调用transformTree方法，查找当前根节点的所有子菜单
        // 将子菜单设置到当前根节点的子菜单列表中
        // 递归调用transformTree方法，以当前根节点的menuId作为新的pid参数
        roots.forEach(root -> root.setList(transformTree(menus, root.getMenuId())));
        //返回根菜单集合，现在它们已经包含了各自的子菜单
        return roots;
    }
}
